React-Router
✒️ 2025-05-28 10:26 내용 수정
React-Router
- React에서 각 페이지를 만들고 연결하기 위한 router를 설정할 수 있다.
- 공식 사이트 : https://reactrouter.com/en/main
- v6.4 변경점 : https://reactrouter.com/en/main/start/overview
- JSP의 Controller의 @RequestMapping, Node.js의 app.get()처럼 라우팅을 설정한다.
- 각 페이지의 URL을 누르면 그 내용에 맞는 태그 내용을 출력하는 식으로 작동한다.
- React-router 예제 및 설명 참고 : 갓대희's 작은공간 React Router 사용하기
- 실습에서 사용한 Component 목록
| Component | 설명 |
|---|---|
<BrowserRouter> |
브라우저 주소창에 clean URL을 사용하여 현재 위치를 저장하고 이동을 관리함 |
<Routes> |
어플리케이션 어디에서나 렌더되며, 현재 위치와 맞는 자식 Route를 매칭시킴 |
| Routes Component 안에 Route Component를 작성함 | |
<Route> |
URL과 component들을 이어주고 데이터 로딩과 복제를 연결함 |
| Route 안에 하위 Route를 넣을 수 있음 (/test/child) | |
<Outlet> |
부모 Route element 안의 하위 Route의 element를 렌더링할 위치를 지정함 |
<Link> |
a태그 대신 React에서 사용하며, 클릭 시 다른 페이지로 이동시켜주는 역할 |
<Navigate> |
Link와 비슷하게 렌더링 시 현재의 위치를 바꿔주는 역할. useNavigate Hook으로 대신할 수 있음 |
| button 태그의 onClick등과 함께 사용하여 페이지 이동을 처리할 수 있음 |
- Routes와 Route
import {Routes, Route} from 'react-router-dom';
<Routes>
<Route path='/경로' element={<Component param={param}></Component>}>
<Route path='경로' element={<Component2></Component2>}></Route>
</Route>
<Route path='/경로/:id' element={<Component3 param={param}></Component3>}></Route>
</Routes>
- Link와 useNavigate
import {Link, useNavigate} from 'react-router-dom';
const navigate = useNavigate();
<Link to="/경로"></Link>
<button onClick={()=>{navigate('/경로')}}>이동버튼</button>
<button onClick={()=>{navigate(-1)}}>이전 위치로 이동 버튼</button>
설치 및 사전 설정
-
먼저 각 페이지 및 필요한 부분 별로 Component를 사용하기 위해 파일 및 함수를 분리하여 작성한다.
- src/component : 세부 내용을 작성하는 태그를 출력할 component
- src/data : API, DB로부터 가져오거나, JSON, Javascript 등으로 작성된 데이터
- src/router : 페이지 단위의 component.
- public/image : 이미지 파일
-
VSC 터미널에서
npm i react-router-dom@6로 react-router v6 라이브러리를 다운 받는다. -
index.js : BroswerRouter를 import하고 Component를 추가한다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from 'react-router-dom'; // BrowserRouter 가져오기
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter> {/*BrowserRouter 적용*/}
<App />
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
- (선택사항) Bootstrap : 기존 bootstrap을 설치해서 사용하거나 react-bootstrap을 import해서 사용하는 방식 중 편한 방식으로 적용한다.
- Bootstrap 참고.
- css는 기본 설정과 배경 이미지, 크기 및 hover 등 많은 설정을 적용하진 않았다.
*{margin:0; padding:0; box-sizing: border-box;}
ul, ol, li{list-style: none;}
a{text-decoration: none; color:black;}
.sec{width: 100%;}
.visual{
height: 400px;
background-image: url('../public/image/bg.png');
background-size: cover;
background-position: center;
}
.col{text-align: center;}
.col img{width: 100%;}
.col.item a:hover{color:blue;}
.detail .col{
display: flex; justify-content: center; align-items: center;
text-align: left;
}
.detail img{width: 50%;}
페이지별 라우팅 설정
- 실습은 신발 가게 테마로 진행되었고, 실습 자료는 reference를 얻지 못해 기록하지 못했다.
1. App.js : 전체 Route 설정
- App.js : App에는 Route Component와 Routes Component를 추가하고, path에 경로 이름을 작성하고 element에 태그를 작성한다.
- Route 태그에 지정한 path를 href나 Link Component에 작성하면 해당 라우팅을 통해 연결되는 것은 Route 태그에 있는 element의 출력 내용이다.
- 페이지를 각각 Main, Detail, About, Error Component로 나누어 페이지별 Component를 만들었다.
- 각 페이지는 src/router 에 저장하고, 세부 내용을 출력하는 Component(즉 페이지 외의 Component)는 src/component 폴더에 저장했다.
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import data from './data/data.js';
import { useState } from 'react';
import { Routes, Route, Link, Outlet } from 'react-router-dom';
import Main from './router/Main';
import Header from './component/Header';
import Detail from './router/Detail';
import About from './router/About';
import Event from './router/Event';
function App() {
const imageName = '/image/shoe';
const [items, setItems] = useState(data);
return (
<>
{/* Header는 Component로 만들어 가져온다 */}
<Header></Header>
<section className='visual'></section>
<Routes> {/* Routes 내의 Route를 추가한다. */}
{/* 라우팅할 경로, 출력할 element(Component)를 작성한다. */}
<Route path='/' element={<Main items={items} imageName={imageName}></Main>}></Route>
{/* detail의 경우 parameter로 id를 받는다 */}
<Route path='/detail/:id' element={<Detail items={items} imageName={imageName}/>}></Route>
{/* about은 하위 경로로 /about/member, /about/location 을 가진다 */}
<Route path='/about' element={<About />}>
{/* 하위 경로에는 /를 작성하지 않고, About.js의 <Outlet>에 element가 출력된다 */}
<Route path='member' element={<div>members</div>}></Route>
<Route path='location' element={<div>location</div>}></Route>
</Route>
{/* event도 about처럼 /event/one, /event/two를 하위 경로로 가진다 */}
<Route path='/event' element={<Event />}>
{/* Event.js의 <Outlet>에 element가 출력된다 */}
<Route path='one' element={<div>첫 주문 시 10% 할인</div>}></Route>
<Route path='two' element={<div>생일기념 쿠폰받기</div>}></Route>
</Route>
{/* 요청이 잘못된 경우엔 에러를 알려주는 문구를 렌더링한다 */}
<Route path="*" element={<h2>404! 요청하신 페이지가 없습니다!</h2>}></Route>
</Routes>
</>
);
}
export default App;
2. Header : 페이지 이동 설정
- Header.js : 사이트의 navigation을 만드는 Component로, react-bootstrap의 Nav와 Dropdown과 관련된 Component들로 만들었다.
- react-bootstrap nav : React Bootstrap Navs and tabs
- react-bootstrap dropdown : React Bootstrap Dropdowns
- Nav의 href에는 라우팅된 경로들을 작성하여 해당 요소를 누를 때 특정 페이지로 이동하도록 설정했다.
import 'bootstrap/dist/css/bootstrap.min.css'; // react-bootstrap 사용 시 두 줄 모두 추가해야 한다
import {Navbar, Container, Nav, NavDropdown} from 'react-bootstrap';
function Header() {
return(
<header>
<Navbar bg="dark" data-bs-theme="dark">
<Container>
{/* 메인 페이지의 경로를 추가 */}
<Navbar.Brand href="/">Shoes Shop</Navbar.Brand>
<Navbar.Toggle aria-controls="dropdown" />
<Navbar.Collapse id="dropdown">
<Nav>
<NavDropdown
id="nav-about-dropdown"
title="About"
menuVariant="dark"
>
{/* href에 라우팅 경로를 입력하며, 하위 경로까지 입력하면 해당 Component를 렌더링할 수 있다. */}
<NavDropdown.Item href="/about/member">멤버</NavDropdown.Item>
<NavDropdown.Item href="/about/location">위치</NavDropdown.Item>
</NavDropdown>
</Nav>
<Nav>
<NavDropdown
id="nav-event-dropdown"
title="Event"
menuVariant="dark"
>
<NavDropdown.Item href="/event/one">서비스</NavDropdown.Item>
<NavDropdown.Item href="/event/two">생일</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</header>
)
}
export default Header;
3. 메인 페이지 : parameter를 통한 페이지 이동 추가
- Main.js : 사이트의 메인 페이지로, 상품 목록을 볼 수 있다.
- 하위 Component로 Item을 포함하며, Item Component에 데이터를 전달해준다.
- 라우팅 설정으로 인해 URL에 "/"를 입력하면 Main 페이지가 렌더링된다.
import Item from '../component/Item';
function Main(props) {
// 전달 받은 데이터를 다시 Item Component에 넘겨준다
let {items, imageName} = props;
return(
<>
{/* Main 페이지에 출력될 내용 */}
<section className='sec'>
<div className='container'>
<div className='row'>
{
items.map((el, i)=>{
return(
{/* 상세 내용은 Item Component에서 작성해서 출력한다 */}
<Item key={el.id} i={i} item={el} imageName={imageName}></Item>
)
})
}
</div>
</div>
</section>
</>
)
}
export default Main;
- /src/component/Item.js : 상품 목록을 출력하는 Component로, Main의 하위 Component다.
- 여기에서 Link Component를 통해 상품 상세 페이지인 /detail로 넘어간다.
- 이미지 파일 등을 사용할 때
process.env.PUBLIC_URL는 서버 상의 public 폴더 경로이므로, 배포 등을 할 때 로컬 경로 대신 사용해야 한다.
import { Link } from "react-router-dom";
function Item(props) {
{/* Main Component로부터 받은 데이터를 사용해 상세 내용을 출력한다 */}
let {i, item, imageName} = props;
let {id, title, content, price} = item;
return(
<div className='col md-4 item'>
{/* process.env.PUBLIC_URL 는 웹 상에서 파일 경로를 찾을 때 사용 */}
<Link to={'/detail/'+id}>
<img src={process.env.PUBLIC_URL + imageName + (id) + '.jpg'} alt={imageName[i]}></img>
<h3>{title}</h3>
<p>
<span className='d-block'>{content}</span>
</p>
</Link>
</div>
)
}
export default Item;
4. 상세보기 : parameter를 받아 상세 데이터 조회
- Detail.js : 상품별 상세보기를 제공하는 Component다.
- URL에서 parameter를
/detail/:id형식으로 받으며, 해당 parameter에 해당하는 상품의 정보를 DetailItem이라는 하위 Component에게 전달해 내용을 렌더링한다. useEffect()를 사용하여 2초 뒤에 사라지는 배너를 만들었다.- useEffect 참고.
- URL에서 parameter를
import { useParams } from "react-router-dom";
import { useState, useEffect } from "react";
import DetailItem from '../component/DetailItem';
function Detail(props) {
{/* App Component로부터 전달 받은 데이터를 사용
useParams로 URL에 포함된 parameter를 받을 수 있다
*/}
let {id} = useParams();
let {items, imageName} = props;
let item = items.find((el)=>(el.id == id)); {/* 조건을 만족하는 요소를 반환 */}
let [banner, setBanner] = useState(true); {/* 배너가 보이는지 저장하는 state 변수 */}
useEffect(()=>{ {/* 처음 페이지 렌더링 후 1회만 effect를 실행하고, clean-up으로 setTimeout을 제거한다 */}
let timer = setTimeout(()=>{
setBanner(false);
}, 2000);
return ()=>clearTimeout(timer);
},[])
return(
<>
{
(banner) ? <div className="banner">
2초 후 사라지는 배너
</div> : null
}
<section className='sec detail'>
<div className='container'>
<div className='row row-cols-1'>
{/* 상세 내용은 DetailItem Component에서 만들어 출력한다 */}
<DetailItem key={id} id={id} item={item} imageName={imageName}></DetailItem>
</div>
</div>
</section>
</>
)
}
export default Detail;
- /src/component/DetailItem.js : Detail.js의 하위 Component로 URL parameter와 상품 정보를 Detail로부터 전달 받아 데이터를 출력하는 Component다.
import {Button} from 'react-bootstrap'; // react-bootstrap Button Component
function DetailItem(props) {
{/* Detail로부터 전달 받은 데이터를 가져온다 */}
let {id, item, imageName} = props;
let {title, content, price} = item;
return(
<div className='col mt-4'>
{/* process.env.PUBLIC_URL 는 웹 상에서 파일 경로를 찾을 때 사용 */}
<img src={process.env.PUBLIC_URL + imageName + (id) + '.jpg'} alt={title}></img>
<div className="info">
<h3>{title}</h3>
<p>
<span className='d-block'>{content}</span>
<span className='d-block'>{price} Won</span>
</p>
<button className="btn btn-danger">주문하기</button>
{/* react-bootstrap의 Button Component 중 href는 a 태그를 버튼 형식으로 만든 것이다 */}
<Button href='/'>메인으로</Button>
</div>
</div>
)
}
export default DetailItem;
5. 이벤트와 About : 하위 path의 내용을 Outlet으로 출력
- Event.js : 이벤트 내용을 보여주는 페이지로, 라우팅 실습을 위해 간단한 내용만 추가했다.
- React-Router#4. 상세보기 parameter를 받아 상세 데이터 조회와 다르게 URL에 parameter를 받지 않지만, 하위 path를 가지고 있다.
- 하위 경로의 내용을 출력할 때 부모 경로의 Component에서
<Outlet>을 추가해야 해당 위치에서 렌더링된다.
import 'bootstrap/dist/css/bootstrap.min.css';
import { Outlet } from 'react-router-dom';
function Event(props) {
return(
<>
<section className='sec about'>
<div className='container'>
<h2>오늘의 이벤트</h2>
{/* Event의 하위 페이지들은 Outlet을 작성하면 나옴 */}
<Outlet></Outlet>
</div>
</section>
</>
)
}
export default Event;
- About.js도 Event.js처럼 Route에 하위 Route를 가지고 있으며, URL에
/about/member이나/about/location과 같은 요청을 입력하면 element 내의 태그가 렌더링된다.
import { Outlet } from 'react-router-dom';
function About(props) {
return(
<>
<section className='sec about'>
<div className='container'>
<h2>About</h2>
{/* About의 하위 페이지들은 Outlet을 작성하면 나옴 */}
<Outlet></Outlet>
</div>
</section>
</>
)
}
export default About;